﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Mime;
using System.Runtime.Remoting;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using GurLoader.Preferences;
using GurLoader.Util;
using GurLoader.ViewModel;
using Moq;
using NUnit.Framework;

namespace GurLoaderWPF.Test.ViewModel
{
    [TestFixture]
    public class PreferencesViewModelTests
    {
        [Test]
        public void TestCreateInstance()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x=>x.BeginEdit()).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);
            mockPreferences.VerifyAll();
        }

        [Test]
        public void TestIsDirtyRequeriesCommandBindings()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            mockCommandManager.Setup(x=>x.InvalidateRequerySuggested()).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);

            // preferences changing dirty state should trigger a command manager requery
            mockPreferences.Raise(x=>x.PropertyChanged += null, new PropertyChangedEventArgs("IsDirty"));
            
            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void TestOtherPropertyChangeDoesNotRequeriesCommandBindings()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            
            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);

            // raise property change for anything OTHER than IsDirty
            mockPreferences.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs("SomeOtherProperty"));

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void TestOkCommandCanExecute()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            mockPreferences.Setup(x=>x.IsDirty).Returns(true).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);
            Assert.IsNotNull(vm.OkCommand);
            Assert.IsTrue(vm.OkCommand.CanExecute(null));

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void TestOkCommandCanNotExecute()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            mockPreferences.Setup(x => x.IsDirty).Returns(false).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);
            Assert.IsNotNull(vm.OkCommand);
            Assert.IsFalse(vm.OkCommand.CanExecute(null));

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void TestOkCommandExecuted()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            mockPreferences.Setup(x=>x.EndEdit()).Verifiable();
            mockPreferences.Setup(x=>x.Save()).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);
            
            ManualResetEvent mre = new ManualResetEvent(false);
            bool requestCloseCalled = false;

            vm.RequestClose += (sender, e) =>
            {
                requestCloseCalled = true;
                mre.Set();
            };

            Assert.IsNotNull(vm);
            Assert.IsNotNull(vm.OkCommand);

            vm.OkCommand.Execute(null);

            mre.WaitOne(1000*10); // 10 second max wait

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
            Assert.IsTrue(requestCloseCalled);
        }

        [Test]
        public void TestCancelCommandCanExecute()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);
            Assert.IsNotNull(vm.CancelCommand);
            Assert.IsTrue(vm.CancelCommand.CanExecute(null));

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void TestCancelCommandExecuted()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            mockPreferences.Setup(x => x.CancelEdit()).Verifiable();

            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            ManualResetEvent mre = new ManualResetEvent(false);
            bool requestCloseCalled = false;

            vm.RequestClose += (sender, e) =>
            {
                requestCloseCalled = true;
                mre.Set();
            };

            Assert.IsNotNull(vm);
            Assert.IsNotNull(vm.CancelCommand);

            vm.CancelCommand.Execute(null);

            mre.WaitOne(1000 * 10); // 10 second max wait

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
            Assert.IsTrue(requestCloseCalled);
        }

        [Test]
        public void TestDispose()
        {
            Mock<GurLoaderPreferences> mockPreferences = new Mock<GurLoaderPreferences>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();

            mockPreferences.Setup(x => x.BeginEdit()).Verifiable();
            
            PreferencesViewModel vm = new PreferencesViewModel(mockPreferences.Object, mockCommandManager.Object);

            Assert.IsNotNull(vm);

            vm.Dispose();

            // normalls raising IsDirty property change triggers a command bindings requery, but since 
            // dispose should remove the event handler we expect this not to happen
            // and therefore have not provided a "Setup" for that call above.

            // raise the event and verify nothing is called back
            mockPreferences.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs("IsDirty"));

            mockPreferences.VerifyAll();
            mockCommandManager.VerifyAll();
        }


    }
}
